/**
 * 
 */
package org.dse.index;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.dse.analysis.Token;
import org.dse.beans.HtmlDocument;
import org.dse.beans.KeywordIndex;
import org.dse.dao.KeywordIndexDAO;
import org.dse.html.HtmlParser;
import org.dse.util.ApplicationContextUtil;
import org.dse.util.Constants;

/**
 * This class implements the logic to add keyword indexes of a document. It uses
 * database as the permanent store.
 * 
 * The DbIndexWriter uses a Map as the data structure. The key is the keyword
 * and the object associated with it is the index.
 * 
 * The addToken() method prepares the tokens related to the document, set the
 * properties of the indexes, and the createIndex() method stores them.
 * 
 * Each DbIndexWriter object is related to each html document.
 * 
 * @author zhangchen
 * 
 */
public class DbIndexWriter implements IndexWriter {

	private HtmlParser parser;// the HtmlParser on the current processed
								// document

	private HtmlDocument document;// the current processed document

	private Map<String, KeywordIndex> keywordMap = new HashMap<String, KeywordIndex>();

	private static Properties tagScoreProps;// tag score properties from the
											// file

	private static KeywordIndexDAO keywordIndexDao;

	// tag score properties file
	private static final String SCORE_FILE = "/tagscore.properties";

	static {
		// get the bean from the application context
		keywordIndexDao = (KeywordIndexDAO) ApplicationContextUtil
				.getBean("KeywordIndexDAO");

		InputStream is = DbIndexWriter.class.getResourceAsStream(SCORE_FILE);

		tagScoreProps = new Properties();
		try {
			tagScoreProps.load(is);
		} catch (IOException e) {
			System.err.println(e.getMessage());
		}
	}

	/**
	 * The constructor.
	 * 
	 * @param parser
	 *            the HtmlParser object giving some parse result
	 * @param document
	 *            the current processed html document
	 */
	public DbIndexWriter(HtmlParser parser, HtmlDocument document) {
		this.parser = parser;
		this.document = document;
	}

	/**
	 * Add a token into the data structure and create an index object.
	 * 
	 * @param token
	 *            the token to be added
	 */
	public void addToken(Token token) {
		KeywordIndex index = null;
		if (keywordMap.keySet().contains(token.getContent()))// if the token
																// is added
																// earlier
			index = keywordMap.get(token.getContent());
		else {// if the token is a new token
			index = new KeywordIndex();
			index.setKeyword(token.getContent());
			index.setAppearTimes(0);
			index.setPositions("");
			index.setClickTimes(0);
		}

		index.setAppearTimes(index.getAppearTimes() + 1);
		if (index.getPositions().equals(""))
			index.setPositions("" + token.getStartPos());
		else
			index
					.setPositions(index.getPositions() + ","
							+ token.getStartPos());

		keywordMap.put(token.getContent(), index);// add to the data structure
	}

	/**
	 * Get the indexes from the data structure. Store all the indexes to the
	 * database in batch mode.
	 */
	public void createIndex() {
		int iter = 0;

		for (String keyword : keywordMap.keySet()) {
			int tagScore = calculateTagScore(keyword);
			KeywordIndex index = keywordMap.get(keyword);
			index.setTagScore(tagScore);
			index.setHtmlDocument(document);// some left properties
			keywordIndexDao.makePersistent(index);

			if (iter % Constants.BATCH_SIZE == 0) {
				keywordIndexDao.flush();
				keywordIndexDao.clear();
			}

			++iter;
		}
	}

	/**
	 * Some tags give a keyword additional score, and the score is treated as a
	 * rank in the query process. This method calculates the score of a keyword
	 * in the current html document.
	 * 
	 * @param keyword
	 *            the keyword
	 * @return the score of this keyword
	 */
	private int calculateTagScore(String keyword) {
		int tagScore = 0;
		Set<Object> keyset = tagScoreProps.keySet();
		for (Object o : keyset) {
			String key = (String) o;
			Set<String> set = parser.get(key);// parser tells whether the key
												// is in specific tags
			for (String s : set) {
				if (s.indexOf(keyword) != -1)
					tagScore += Integer
							.parseInt(tagScoreProps.getProperty(key));
			}
		}

		return tagScore;
	}
}
